﻿using System;
using System.Collections.Generic;
using System.Linq;
using XmlStudio.Controls;
using XmlStudio.Controls.Interfaces;
using System.Windows;
using System.ComponentModel;

namespace XmlStudio.Helper {
    internal class SelectionService : INotifyPropertyChanged {
        #region Fields
        private DesignerCanvas designerCanvas;
        //private static SelectionService instance;
        private static Dictionary<DesignerCanvas, SelectionService> selectionServices = new Dictionary<DesignerCanvas, SelectionService>();
        #endregion

        #region Properties
        public static SelectionService GetCurrentInstance(DesignerCanvas canvas) {
            SelectionService selectionService = null;
            if(selectionServices.TryGetValue(canvas, out selectionService)) {
                return selectionService;
            } else {
                throw new InvalidOperationException("Selection service could not be found.");
            }
        }

        public static SelectionService GetCurrentInstance(DependencyObject element) {
            var canvas = DesignerCanvasFinder.FindParentCanvas(element);
            return GetCurrentInstance(canvas);
        }

        private List<ISelectable> currentSelection;
        internal List<ISelectable> CurrentSelection {
            get { return currentSelection ?? (currentSelection = new List<ISelectable>()); }
        }
        #endregion

        #region Constructors
        protected SelectionService(DesignerCanvas canvas) {
            this.designerCanvas = canvas;
        }
        #endregion

        #region Methods
        public static SelectionService Create(DesignerCanvas canvas) {
            var current = new SelectionService(canvas);
            selectionServices.Add(canvas, current);
            return current;
        }

        internal void SelectItem(ISelectable item) {
            this.ClearSelection();
            this.AddToSelection(item);
        }

        internal void AddToSelection(ISelectable item) {
            if(item is IGroupable) {
                List<IGroupable> groupItems = GetGroupMembers(item as IGroupable);

                foreach(ISelectable groupItem in groupItems) {
                    groupItem.IsSelected = true;
                    CurrentSelection.Add(groupItem);
                }
            } else {
                item.IsSelected = true;
                CurrentSelection.Add(item);
            }

            this.OnPropertyChanged("CurrentSelection");
        }

        internal void RemoveFromSelection(ISelectable item) {
            if(item is IGroupable) {
                List<IGroupable> groupItems = GetGroupMembers(item as IGroupable);

                foreach(ISelectable groupItem in groupItems) {
                    groupItem.IsSelected = false;
                    CurrentSelection.Remove(groupItem);
                }
            } else {
                item.IsSelected = false;
                CurrentSelection.Remove(item);
            }

            this.OnPropertyChanged("CurrentSelection");
        }

        internal void ClearSelection() {
            CurrentSelection.ForEach(item => item.IsSelected = false);
            CurrentSelection.Clear();

            this.OnPropertyChanged("CurrentSelection");
        }

        internal void SelectAll() {
            ClearSelection();
            CurrentSelection.AddRange(designerCanvas.Children.OfType<ISelectable>());
            CurrentSelection.ForEach(item => item.IsSelected = true);

            this.OnPropertyChanged("CurrentSelection");
        }

        internal List<IGroupable> GetGroupMembers(IGroupable item) {
            IEnumerable<IGroupable> list = designerCanvas.Children.OfType<IGroupable>();
            IGroupable rootItem = GetRoot(list, item);
            return GetGroupMembers(list, rootItem);
        }

        internal IGroupable GetGroupRoot(IGroupable item) {
            IEnumerable<IGroupable> list = designerCanvas.Children.OfType<IGroupable>();
            return GetRoot(list, item);
        }

        private static IGroupable GetRoot(IEnumerable<IGroupable> list, IGroupable node) {
            if(node == null || node.ParentID == Guid.Empty) {
                return node;
            }

            return (from item in list where item.ID == node.ParentID select GetRoot(list, item)).FirstOrDefault();
        }

        private static List<IGroupable> GetGroupMembers(IEnumerable<IGroupable> list, IGroupable parent) {
            var groupMembers = new List<IGroupable> { parent };

            var children = list.Where(node => node.ParentID == parent.ID);

            foreach(var child in children) {
                groupMembers.AddRange(GetGroupMembers(list, child));
            }

            return groupMembers;
        }

        private void OnPropertyChanged(string propertyName) {
            var propertyChanged = this.PropertyChanged;
            if(propertyChanged != null) {
                propertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion

        #region Events
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion
    }
}
